NAME = "Caden Truelick"
For this project, I am going to be using a dataset consisting of several thousand images of various fruits and vegetables and I will create machine learning models that can predict what type of fruit or vegetable an image is depicting. Some of the methods that I will use are listed below:
Unsupervised Models
Supervised Models
Here is a link to the dataset: https://www.kaggle.com/datasets/kritikseth/fruit-and-vegetable-image-recognition
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import plotly.express as px
import seaborn as sns
import tensorflow as tf
#for opening/extracting the zipfile
import zipfile
#for loading in custom data
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.preprocessing import image
#where r is read-mode
with zipfile.ZipFile('fruit_data.zip', 'r') as zip_ref:
zip_ref.extractall('data')
from pathlib import Path
import imghdr
#directories of each folder
data_dir_train = 'data/train/'
data_dir_test = 'data/test/'
data_dir_valid = 'data/validation/'
#image extensions
image_extensions = ['.png', '.jpg', '.jpeg']
img_type_accepted_by_tf = ['bmp', 'gif', 'jpeg', 'png']
#check files to make sure it is the correct extension
def check_files(directory):
for filepath in Path(directory).glob('**/*'):
if filepath.suffix.lower() in image_extensions:
img_type = imghdr.what(filepath) #pulls the img extension from the file
if img_type is None:
print(f"{filepath} is not an image")
elif img_type not in img_type_accepted_by_tf:
print(f"{filepath} is a {img_type}, not accepted by TensorFlow")
print(f'Completed {directory}')
check_files(data_dir_train)
check_files(data_dir_test)
check_files(data_dir_valid)
train = tf.keras.utils.image_dataset_from_directory(data_dir_train,
color_mode = 'rgb',
batch_size = 36,
image_size = (256,256),
shuffle=True,
crop_to_aspect_ratio = True)
valid = tf.keras.utils.image_dataset_from_directory(data_dir_valid,
color_mode = 'rgb',
batch_size = 36,
image_size = (256,256),
shuffle=False,
crop_to_aspect_ratio = True)
test = tf.keras.utils.image_dataset_from_directory(data_dir_test,
color_mode = 'rgb',
batch_size = 36,
image_size = (256,256),
shuffle=False,
crop_to_aspect_ratio = True)
class_names = train.class_names
print(class_names)
def plotImages(data):
The plotImages function takes a dataset input data. The function takes a batch from the dataset and plots all of the images with the corresponding label.
def plotImages(data):
plt.figure(figsize=(15,15))
for images, labels in data.take(1):
for i in range(36):
ax = plt.subplot(6, 6, i+1)
plt.imshow(images[i].numpy().astype('uint8'))
plt.title(class_names[labels[i]])
plt.axis('off')
plotImages(train)
from sklearn.cluster import KMeans #for clustering analysis
from sklearn.decomposition import PCA #for dimensionality reduction
#flatten the image data
flat_images = []
for images, _ in train:
flat_images.extend(images.numpy().reshape(images.shape[0], -1))
flat_images = np.array(flat_images)
kmeans = KMeans(n_clusters=36, random_state=123, n_init=10)
cluster_labels = kmeans.fit_predict(flat_images)
pca = PCA(n_components=2)
reduced_data = pca.fit_transform(flat_images)
df_2d = pd.DataFrame(data=reduced_data, columns=['PC1', 'PC2'])
df_2d['Cluster'] = cluster_labels
fig = px.scatter(df_2d, x='PC1', y='PC2', color='Cluster', opacity=0.8, size_max=10)
fig.update_layout(title='2D K-means Clustering')
fig.show()
pca_3d = PCA(n_components=3)
reduced_data_3d = pca_3d.fit_transform(flat_images)
df_3d = pd.DataFrame(data=reduced_data_3d, columns=['PC1', 'PC2', 'PC3'])
df_3d['Cluster'] = cluster_labels
fig = px.scatter_3d(df_3d, x='PC1', y='PC2', z='PC3', color='Cluster', opacity=0.8, size_max=10)
fig.update_layout(scene=dict(xaxis_title='PC 1', yaxis_title='PC 2', zaxis_title='PC 3'),
title='3D K-means Clustering')
fig.show()
#choose a cluster to display (minimum 5 images)
cluster_to_display = 0
for i in cluster_labels:
images_in_cluster = np.where(cluster_labels == i)[0]
num_images = len(images_in_cluster)
if num_images >= 5:
cluster_to_display = i
break
#get the indicies in the cluster
images_in_cluster = np.where(cluster_labels == cluster_to_display)[0]
#display images from chosen cluster
plt.figure(figsize=(15, 5))
for i in range(5): #displaying first 5 images
image_idx = images_in_cluster[i]
plt.subplot(1, 5, i + 1)
#reshape back to original
original_shape = train.element_spec[0].shape[1:] # Get the shape of original images
image = flat_images[image_idx].reshape(original_shape).astype('uint8')
plt.imshow(image)
plt.title(f'Cluster {cluster_to_display}')
plt.axis('off')
plt.show()
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dropout, Dense, Reshape, Rescaling
model = tf.keras.Sequential()
model.add(Rescaling(1./255, input_shape=(256,256,3)))
model.add(Conv2D(16, (3, 3), 1, activation='relu'))
model.add(MaxPooling2D((2, 2)))
model.add(Conv2D(32, (3, 3), 1, activation='relu'))
model.add(MaxPooling2D((2, 2)))
model.add(Conv2D(64, (3, 3), 1, activation='relu'))
model.add(MaxPooling2D((2, 2)))
model.add(Flatten())
model.add(Dense(256, activation='relu'))
model.add(Dense(36, activation='softmax')) #softmax because this is a multiclass classification
model.summary()
model.compile(optimizer='adam',
loss = 'sparse_categorical_crossentropy', #labels are provided as integers so we use sparse
metrics=['accuracy'])
history = model.fit(train, epochs=10, batch_size=36, validation_data=valid)
test_loss, test_acc = model.evaluate(valid)
Below are plots of the training loss and accuracy then the validation loss and accuracy, respectively.
#adapted from hw5
df = pd.DataFrame(history.history)
fig, ax1 = plt.subplots(2,1,figsize=(10,8))
ax1[0].plot(df['loss'], label='Training Loss')
ax1[0].plot(df['val_loss'], label='Validation Loss')
ax1[0].set_xlabel('Epoch')
ax1[0].legend()
ax1[1].plot(df['accuracy'], label='Training Accuracy')
ax1[1].plot(df['val_accuracy'], label='Validation Accuracy')
ax1[1].set_xlabel('Epoch')
ax1[1].legend()
pred = model.predict(test)
y_pred = np.argmax(pred, axis=1)
print(y_pred)
from sklearn.metrics import confusion_matrix
#get true labels from test dataset
true_labels = []
for _, labels in test:
true_labels.extend(labels.numpy())
conf_matrix = confusion_matrix(true_labels, y_pred)
plt.figure(figsize=(15, 15))
sns.heatmap(conf_matrix, annot=True, fmt='d', cmap='Blues', xticklabels=class_names, yticklabels=class_names)
plt.xlabel('Predicted Labels')
plt.ylabel('True Labels')
plt.title('Confusion Matrix')
plt.show()
def plot_images_with_labels(data, true_labels, predicted_labels, class_names):
plt.figure(figsize=(15, 20))
unique_classes = np.unique(true_labels)
for i, class_label in enumerate(unique_classes):
# Find an image with the true label from the current class
true_label_idx = np.where(true_labels == class_label)[0][0]
# Get the image tensor from the dataset
image_tensor = None
for images, labels in data:
if class_label in labels.numpy():
image_tensor = images[labels.numpy() == class_label][0]
break
# Find an image with the predicted label from the current class
predicted_label_idx = np.where(predicted_labels == class_label)[0][0]
# Plot the image with true and predicted labels
plt.subplot(6, 6, i + 1)
plt.imshow(image_tensor.numpy().astype('uint8'))
true_class = class_names[true_labels[true_label_idx]]
predicted_class = class_names[predicted_labels[true_label_idx]] # Corrected index
plt.title(f'True: {true_class}\nPredicted: {predicted_class}')
plt.axis('off')
# Plot one image per class with true and corrected predicted labels
plot_images_with_labels(test, true_labels, y_pred, class_names)
plt.show()
def plot_incorrect_predictions(data, true_labels, predicted_labels, class_names):
plt.figure(figsize=(15, 15))
incorrect_indices = np.where(true_labels != predicted_labels)[0]
for i, incorrect_idx in enumerate(incorrect_indices):
image_tensor = None
for images, labels in data:
if predicted_labels[incorrect_idx] in labels.numpy():
image_tensor = images[labels.numpy() == predicted_labels[incorrect_idx]][0]
break
plt.subplot(4, 5, i + 1)
plt.imshow(image_tensor.numpy().astype('uint8'))
true_class = class_names[true_labels[incorrect_idx]]
predicted_class = class_names[predicted_labels[incorrect_idx]]
plt.title(f'True: {true_class}\nPredicted: {predicted_class}')
plt.axis('off')
# Plot incorrect predictions
plot_incorrect_predictions(test, true_labels, y_pred, class_names)
plt.show()